#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <sys/epoll.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>

#define INFO(fmt,...) printf("file:[%s],line:[%d]"fmt,__FILE__,__LINE__,##__VA_ARGS__);

struct client_info{
    int fd;
    struct sockaddr_in addr;
};

int main(int argc,const char* argv[])
{
    if(argc != 3){
        INFO("argc is invalid,argc must be 3");
        exit(-1);
    }

    int sockfd = -1;
    int acceptfd = -1;
    struct sockaddr_in serveraddr,clientaddr;
    struct client_info clients[100];
    int client_count = 0;
    socklen_t len = sizeof(serveraddr);

    memset(&serveraddr,0,sizeof(serveraddr));
    memset(clients, 0, sizeof(clients));

    sockfd = socket(AF_INET,SOCK_STREAM,0);
    if(sockfd == -1){
        INFO("socket is failed!");
        exit(-1);
    }

    serveraddr.sin_family= AF_INET;
    serveraddr.sin_addr.s_addr=inet_addr(argv[1]);
    serveraddr.sin_port=htons(atoi(argv[2]));

    if(bind(sockfd,(struct sockaddr*)&serveraddr,len)==-1){
        INFO("bind is failed!");
        exit(-1);
    }

    if(listen(sockfd,5)==-1){
        INFO("listen is failed!");
        exit(-1);
    }

    char buf[128]={0};
    struct epoll_event ev,event[100];
    int epfd = -1;
    int nfds = -1;
    int n = 0;
    ssize_t recvbytes;

    epfd = epoll_create(100);
    if(epfd==-1){
        INFO("epoll_create is failed!");
        exit(-1);
    }

    ev.data.fd = sockfd;
    ev.events = EPOLLIN;

    if(epoll_ctl(epfd,EPOLL_CTL_ADD,sockfd,&ev)==-1){
        INFO("epoll_ctl is failed!");
        exit(-1);
    }

    while(1){
        if((nfds=epoll_wait(epfd,event,100,-1))==-1){
            INFO("epoll_wait is failed!");
            exit(-1);
        }
        for(int i=0;i<nfds;i++){
            if(event[i].data.fd == sockfd && event[i].events & EPOLLIN){
                acceptfd=accept(sockfd,(struct sockaddr*)&clientaddr,&len);
                if(acceptfd == -1){
                    INFO("accept is failed");
                    exit(-1);
                }
                printf("Client:[%s,%d]Linked\n",inet_ntoa(clientaddr.sin_addr),ntohs(clientaddr.sin_port));
                send(acceptfd,&acceptfd,sizeof(acceptfd),0);

                ev.data.fd = acceptfd;
                if(epoll_ctl(epfd,EPOLL_CTL_ADD,acceptfd,&ev)==-1){
                    INFO("epoll_ctl is failed!");
                    exit(-1);
                }
                clients[client_count].fd = acceptfd;
                clients[client_count].addr = clientaddr;
                client_count++;
            }else if(event[i].events & EPOLLIN){
                memset(buf,0,sizeof(buf));
                int j = 0;
                for(;j<client_count;j++){
                if(event[i].data.fd == clients[j].fd){
                        break;
                    }
                }
                recvbytes=recv(event[i].data.fd,buf,sizeof(buf),0);
                if(recvbytes==-1){
                    INFO("recv is failed");
                    exit(-1);
                }else if(recvbytes==0||strcmp(buf,"**quit**")==0){
                    printf("Client:[%s,%d]Exit\n",inet_ntoa(clients[j].addr.sin_addr),ntohs(clients[j].addr.sin_port));
                    if(epoll_ctl(epfd, EPOLL_CTL_DEL, event[i].data.fd, &ev) == -1){
                        INFO("epoll_ctl error");
                        exit(-1);
                    }
                    close(event[i].data.fd);
                    clients[j]=clients[--client_count];
                    continue;
                }
                
                printf("Client:[%s,%d]buf=%s\n",inet_ntoa(clients[j].addr.sin_addr),ntohs(clients[j].addr.sin_port),buf);
                strcat(buf,"^_^");
                send(event[i].data.fd,buf,sizeof(buf),0);
            }
        }
    }
    return 0;
}